#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <pthread.h>
#include "cJSON.h"
#define N 64
#define UDP_PORT 8888
#define TCP_PORT 8885

// 定义线程参数结构体
struct thread_args
{
    struct sockaddr_in dev_addr; // 设备地址信息
    int file_port;               // 文件传输端口
};
void *update_thread(void *arg);

int main(int argc, char const *argv[])
{
    // 创建socket
    int broadfd;

    // 创建一个socket文件描述符
    broadfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (broadfd < 0)
    {
        perror("sock err");
        return -1;
    }
    printf(" UDP socket success!\n");
    ssize_t len;
    char buf[N] = {0};
    struct sockaddr_in broad_addr;

    broad_addr.sin_family = AF_INET;
    broad_addr.sin_port = htons(UDP_PORT);                    // 端口号
    broad_addr.sin_addr.s_addr = inet_addr("192.168.51.255"); // IP

    socklen_t addrlen = sizeof(broad_addr);

    // 打开广播设置，允许发送广播包
    int on = 1;
    if (setsockopt(broadfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0)
    {
        perror("set err");
        return -1;
    }

    // 发送广播包－携带搜索协议 "coffee"代表公司名
    ssize_t ret = sendto(broadfd, "Zhiqing", 7, 0, (struct sockaddr *)&broad_addr, addrlen);
    if (ret < 0)
    {
        perror("send to err");
        return -1;
    }
    printf("搜索协议发送成功!\n");

    // 如果对方回复"yes"，那么接收对方的地址 dev_addr
    struct sockaddr_in dev_addr;
    memset(buf, 0, N);
    recvfrom(broadfd, buf, N, 0, (struct sockaddr *)&dev_addr, &addrlen); // 发送端的网络信息
    if (strcmp(buf, "yes") != 0)
    {
        printf("不是我工厂的设备\n");
        return -1;
    }
    else
    {
        printf("与本厂的设备已连接\n");
    }
    // 设备校验通过
    // soft 此时作为TCP客户端和设备建立TCP长连接
    // 建立TCP连接
    int tcpfd = socket(AF_INET, SOCK_STREAM, 0);
    if (tcpfd < 0)
    {
        perror("sock err");
        return -1;
    }
    printf(" TCP socket success!\n");

    // dev_addr.sin_port = htons(8887);//如果不复用UDP的端口号，可以做更改
    // 请求链接服务器 connect
    if (connect(tcpfd, (struct sockaddr *)&dev_addr, addrlen) < 0)
    {
        perror("connect err");
        return -1;
    }
    printf("TCP长连接已建立!\n");

    // 传递文件/命令 send
    // 发送升级问询包 type=1  序列化
    cJSON *root = cJSON_CreateObject();
    cJSON_AddItemToObject(root, "type", cJSON_CreateNumber(1));
    cJSON *data = cJSON_CreateObject();
    cJSON_AddItemToObject(data, "version", cJSON_CreateString("v1.0.1"));       // 表示是一个升级问询包
    cJSON_AddItemToObject(data, "file_name", cJSON_CreateString("update.zip")); // 当前升级包的版本
    cJSON_AddItemToObject(data, "file_len", cJSON_CreateNumber(385));           // 升级包的大小，单位字节
    // cJSON_AddItemToObject(data, "md5", cJSON_CreateString("AABBCCDDEEFF"));     // 文件唯一md5码
    //  计算实际MD5
    char md5_cmd[256];
    char calculated_md5[33] = {0};
    FILE *md5_pipe;
    snprintf(md5_cmd, sizeof(md5_cmd), "md5sum ./update.zip | cut -d' ' -f1");
    md5_pipe = popen(md5_cmd, "r");
    if (md5_pipe)
    {
        fgets(calculated_md5, sizeof(calculated_md5), md5_pipe);
        calculated_md5[strcspn(calculated_md5, "\n")] = 0;
        pclose(md5_pipe);
        cJSON_AddItemToObject(data, "md5", cJSON_CreateString(calculated_md5));
    }
    cJSON_AddItemToObject(root, "data", data);
    char *ptr = cJSON_Print(root); // 生成JSON字符串
    // while (1)
    // {
    if (send(tcpfd, ptr, strlen(ptr), 0) < 0)
    {
        perror("send err");
        // continue;
        return -1;
    }
    printf("Upgrade inquiry sent\n");

    // sleep(1); // 添加发送间隔
    // }
    free(ptr);
    ptr = NULL;
    cJSON_Delete(root); // 释放节点内存

    // 接收客户端发来的 需要升级响应
    char update_buf[1024] = {0};
    size_t recvbyte = recv(tcpfd, update_buf, sizeof(update_buf), 0);
    printf("recvbyte = %ld\n", recvbyte); // 接收到的大小
    if (recvbyte < 0)
    {
        perror("recv err\n");
        return -1;
    }
    // 解析设备端发来的　JSON格式 需要升级命令  反序列化
    printf("Received data: \n");
    printf("%s\n", update_buf);
    cJSON *request_root = cJSON_Parse(update_buf);
    int type = cJSON_GetObjectItem(request_root, "type")->valueint;
    // printf("type= %d\n", type);
    cJSON *request_data = cJSON_GetObjectItem(request_root, "data");
    cJSON *update = cJSON_GetObjectItem(request_data, "update");
    // printf("%s:%s\n", update->string, update->valuestring);

    if (strcmp(update->valuestring, "true") == 0)
    {
        printf("Starting firmware upgrade...\n");
        // 创建线程参数结构体
        struct thread_args *args = malloc(sizeof(struct thread_args));
        if (!args)
        {
            perror("malloc");
            cJSON_Delete(request_root);
            return -1;
        }
        // 复制设备地址信息
        memcpy(&args->dev_addr, &dev_addr, sizeof(dev_addr));
        // 执行升级流程 创建升级线程
        pthread_t tid;
        if (pthread_create(&tid, NULL, update_thread, args) != 0)
        {
            perror("pthread_create err");
            return -1;
        }
        close(tcpfd); // 关闭第一次TCP连接的文件描述符
        cJSON_Delete(request_root);
        pthread_join(tid, NULL); // 阻塞回收线程
    }
    else
    {
        printf("No upgrade needed\n");
    }
    close(broadfd); // 关闭UDP广播的文件描述符
    return 0;
}
// 下发升级包线程函数 - 处理文件传输
void *update_thread(void *arg)
{
    // 获取线程参数
    struct thread_args *args = (struct thread_args *)arg;
    // 创建新 TCP 连接（服务端）
    int upgrade_tcpfd = socket(AF_INET, SOCK_STREAM, 0);
    if (upgrade_tcpfd < 0)
    {
        perror("upgrade socket err");
    }
    printf(" TCP2 socket success!\n");
    // 设置设备地址（使用新的文件传输端口）
    struct sockaddr_in upgrade_addr;
    upgrade_addr.sin_family = AF_INET;
    upgrade_addr.sin_port = htons(TCP_PORT);                       // 指定新端口号用于文件传输
    upgrade_addr.sin_addr.s_addr = args->dev_addr.sin_addr.s_addr; // 只复制IP地址
    // 连接到设备
    if (connect(upgrade_tcpfd, (struct sockaddr *)&upgrade_addr, sizeof(upgrade_addr)) < 0)
    {
        perror("upgrade connect err");
    }
    printf("已连接到设备进行升级包传输!\n");

    // 传输升级包
    //  打开升级文件
    int fd = open("./update.zip", O_RDONLY);
    if (fd < 0)
    {
        perror("open err");
        close(upgrade_tcpfd);
        free(arg);
        return NULL;
    }
    // 传输
    char buffer[4096] = {0};
    ssize_t bytes_read;
    while ((bytes_read = read(fd, buffer, sizeof(buffer))) > 0)
    {
        if (send(upgrade_tcpfd, buffer, bytes_read, 0) < 0)
        {
            perror("send err");
            break;
        }
    }
    close(fd);
    printf("升级包发送成功！\n");

    // 接收客户端发来的 固件传输响应
    char trans_buf[1024] = {0};
    size_t recvbyte = recv(upgrade_tcpfd, trans_buf, sizeof(trans_buf), 0);
    printf("recvbyte = %ld\n", recvbyte); // 接收到的大小
    if (recvbyte < 0)
    {
        perror("recv err\n");
    }
    // 解析设备端发来的　JSON格式 需要升级命令  反序列化
    printf("Received data: \n");
    printf("%s\n", trans_buf);
    cJSON *trans_root = cJSON_Parse(trans_buf);
    cJSON *trans_data = cJSON_GetObjectItem(trans_root, "data");
    int type2 = cJSON_GetObjectItem(trans_root, "type")->valueint;
    // printf("type= %d\n", type);
    cJSON *trans = cJSON_GetObjectItem(trans_data, "trans_success");
    // printf("%s:%s\n", trans->string, trans->valuestring);
    if (strcmp(trans->valuestring, "true") == 0)
    {
        printf("固件传输成功！\n");
    }
    else
    {
        printf("固件传输失败！\n");
    }
    cJSON_Delete(trans_root);

    // 接收客户端发来的 升级响应
    char update_buf[1024] = {0};
    size_t recvbyte1 = recv(upgrade_tcpfd, update_buf, sizeof(update_buf), 0);
    printf("recvbyte = %ld\n", recvbyte); // 接收到的大小
    if (recvbyte1 < 0)
    {
        perror("recv err\n");
    }
    // 解析设备端发来的　JSON格式 需要升级命令  反序列化
    printf("Received data: \n");
    printf("%s\n", update_buf);

    cJSON *update_root = cJSON_Parse(update_buf);
    cJSON *update_data = cJSON_GetObjectItem(update_root, "data");
    int type3 = cJSON_GetObjectItem(update_root, "type")->valueint;
    // printf("type= %d\n", type);
    cJSON *update = cJSON_GetObjectItem(update_data, "update_success");
    // printf("%s:%s\n", trans->string, trans->valuestring);
    if (strcmp(update->valuestring, "true") == 0)
    {
        printf("OTA升级成功!\n");
    }
    else
    {
        printf("OTA升级失败!\n");
    }
    cJSON_Delete(update_root);

    close(upgrade_tcpfd); // 关闭第二次TCP连接的文件描述符
    // 释放线程参数
    free(arg);
}